/*  ----------------------------------------------------------------------------
 *  Arello Mobile
 *  Fast 3d Engine
 *  ----------------------------------------------------------------------------
 * Except where otherwise noted, this work is licensed under
 * http://creativecommons.org/licenses/by/3.0
 */
#pragma once

#include <input/hard/Hardware.h>
#include <input/Manager.h>
#include <NoCopy.h>
#define DIRECTINPUT_VERSION 0x0800
#include <dinput.h>
#include <map>
#include <math.h>

namespace liba
{
namespace input
{
namespace hard
{
namespace directx8
{
class ForceEffect;


class Joystick : public Hardware, private Nocopy
{
public:
	Joystick( IDirectInputDevice8 * device, const std::string & name );
	virtual ~Joystick();

	virtual std::string get_name()const;
	virtual Atom get_type()const
	{
		return Manager::joystick_type_name;
	}

	void poll( int ms_count );

	virtual double get_axis_position( const std::string & axis_name )const;
	virtual Manager::AxisType get_axis_type( const std::string & axis_name )const;

//	virtual bool ChangeAttribute( const Atom & name, double value );

	bool is_valid()const
	{
		return valid;
	}

//	static const Atom Volume;

protected:
	virtual hard::ForceEffect * create_force_effect(const filesystem::String & effect_name);

private:
	IDirectInputDevice8 * device;
	std::string name;

	class PressStrategy
	{		
	public:
		virtual bool get_state( double value )const
		{
			if( fabs( value ) > 0.5 )
				return true;
			
			return false;
		}
	};

	class PressStrategyPOV : public PressStrategy
	{
	public:
		virtual bool get_state( double value )const
		{
			if( abs(value - -1) < std::numeric_limits<double>::epsilon() )
				return true;
			
			return false;
		}
	};

	class AxisState
	{
		std::string device_name;
		std::string axis_name;
		Manager::AxisType axis_type;
		double value;
		bool prev_state;
		PressStrategy * press_strategy;
	public:
		AxisState( Manager::AxisType axis_type, const std::string & device_name, const std::string & axis_name, PressStrategy * press_strategy = 0 )
		:	axis_type( axis_type ),
			axis_name( axis_name ),
			device_name( device_name ),
			value( 0 ),
			prev_state( false ),
			press_strategy( press_strategy )
		{
			if( !press_strategy )
				this->press_strategy = new PressStrategy();
		}

		~AxisState()
		{
			//BUG
//			delete press_strategy; press_strategy = 0;
		}

		void record_value( double new_value )
		{
			value = new_value;
			bool new_state = press_strategy->get_state( new_value );

			if( new_state && !prev_state )
				Manager::add_event( Event( device_name, axis_name, true ) );

			if( !new_state && prev_state )
				Manager::add_event( Event( device_name, axis_name, false ) );

			prev_state = new_state;
		}

		double get_value()const
		{
			return value;
		}

		Manager::AxisType get_axis_type()const
		{
			return axis_type;
		}
	};

	typedef std::map< std::string, AxisState > Axis;
	Axis axis;

	static BOOL CALLBACK ObjectCallback( LPCDIDEVICEOBJECTINSTANCE instance, LPVOID ref );

	AxisState * get_axis( const std::string & axis_name )
	{
		Axis::iterator it = axis.find( axis_name );
		if( it != axis.end() )
			return &it->second;

		return 0;
	}

	typedef std::vector<ForceEffect *> Effects;
	Effects effects;

	bool valid;

	int button_count;
	int pov_count;
	int slider_count;
};

} // namespace directx8
} // namespace hard
} // namespace input
} // namespace liba
using namespace liba;
